package com.summer.ioc.container;

import com.alibaba.fastjson.JSON;
import com.summer.ioc.bean.definition.AbstractBeanDefinition;
import com.summer.ioc.bean.definition.BeanDefinition;
import com.summer.ioc.bean.definition.json.AbstractJsonBeanDefinition;
import com.summer.ioc.bean.definition.json.DefaultJsonBeanDefinition;
import com.summer.ioc.bean.definition.json.configuration.*;
import com.summer.ioc.exception.BeanException;
import com.summer.ioc.reflect.Parameter;
import com.summer.ioc.util.CollectionUtils;
import com.summer.ioc.util.MethodUtil;
import com.summer.ioc.util.StringUtils;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;

/**
 * @author xiaoye
 * @create 2021-04-29 14:20
 */
public class JsonIocContainer extends AbstractIocContainer{

    public JsonIocContainer(String configFilePath)
    {
        initContainer(configFilePath);

        init();
    }

    private void initContainer(String configFilePath) {
        container = new HashMap<>();
        var configFile = new File(configFilePath);
        String configContent = null;
        try {
            configContent = FileUtils.readFileToString(configFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
        var iocConfiguration = JSON.parseObject(configContent, IocConfiguration.class);
        var beans = iocConfiguration.getBeans();
        for (var bean : beans) {
            BeanDefinition bd = castBeanConfigurationToBeanDefinition(bean);
            registryBean(bd);
        }
        var componentScan = iocConfiguration.getComponentScan();
        String[] basePackages = componentScan.getBasePackages();
        if (basePackages.length > 0 && StringUtils.hasText(basePackages[0]))
        {
            scanComponent(basePackages);
        }
    }

    protected Object buildBeanByProperties(String beanName, Class beanClass, List<BeanPropertyConfiguration> propertyConfigurations) {
        Object bean = null;
        //创建bean
        try {
            bean = beanClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        if (bean == null)
            return null;

        //为方便查找method，构建一个methodMap
        Method[] methods = beanClass.getMethods();
        var methodMap = new HashMap<String,Method>();
        for (Method method : methods) {
            methodMap.put(method.getName(),method);
        }

        // setter注入
        for (var propertyConfiguration : propertyConfigurations) {
            //获取setter方法
            var propertyName = propertyConfiguration.getName();
            var setterName = "set" + StringUtils.firstLetterToUpperCase(propertyName);
            Method setter = methodMap.get(setterName);
            // 获取方法参数类型
            var paramType = setter.getParameters()[0].getType();
            try {
                //注入
                if (StringUtils.hasLength(propertyConfiguration.getRef()))
                    setter.invoke(bean, getBean(propertyConfiguration.getRef()));
                else if (StringUtils.hasLength(propertyConfiguration.getValue()))
                    setter.invoke(bean, suitType(propertyConfiguration.getValue(), paramType));
            }catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        return bean;
    }


    private BeanDefinition castBeanConfigurationToBeanDefinition(BeanConfiguration beanConfiguration) {
        DefaultJsonBeanDefinition bd = null;
        try {
            bd = new DefaultJsonBeanDefinition(beanConfiguration.getName(),Class.forName(beanConfiguration.getBeanClass()));
        } catch (ClassNotFoundException e) {
            throw new BeanException(e.getMessage());
        }
        BeanSettingConfiguration settings = beanConfiguration.getSettings();
        bd.setConstructorArgs(settings.getConstructorArgs());
        bd.setProperties(settings.getProperties());
        bd.setLazyLoad(beanConfiguration.isLazyLoad() == null
                ? false
                : beanConfiguration.isLazyLoad());
        bd.setScope(beanConfiguration.getScope() != null
                ? beanConfiguration.getScope()
                : AbstractBeanDefinition.BeanScope.SINGLETON);

        return bd;
    }


    protected void init() {
        initialBeans();
    }

    protected Object buildBeanByConstructor(String beanName,Class beanClass, List<BeanConstructorArgConfiguration> constructorArgConfigurations) {
        Object bean = null;
        Constructor[] constructors = beanClass.getConstructors();
        Constructor constructor = null;
        // 查找参数数目符合给定数目的构造器
        for (Constructor constructor1 : constructors) {
            if (constructor1.getParameterCount() == constructorArgConfigurations.size())
            {
                constructor = constructor1;
                break;
            }
        }
        if (constructor == null)
            throw new BeanException("the bean " + beanName + "need a constructor with " + constructorArgConfigurations.size() + " parameters");

        //构造 构造器 的参数数组
        Parameter[] parameters = MethodUtil.getParameters(constructor);
        Object[] args = new Object[constructor.getParameterCount()];
        for (int i = 0; i < constructor.getParameterCount(); i++) {
            var param = parameters[i];
            var constructorArgConfiguration = constructorArgConfigurations.get(i);
            if (StringUtils.hasLength(constructorArgConfiguration.getRef()))
                args[i] = getBean(constructorArgConfiguration.getRef());
            else
                args[i] = suitType(constructorArgConfiguration.getValue(),param.getParameter().getType());
        }

        //创建bean
        try {
            bean = constructor.newInstance(args);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return bean;
    }



    @Override
    protected Object buildBean(BeanDefinition beanDefinition) {
        var beanDependencyNames = beanDefinition.getDependencyBeanNames();
        buildDependencyBeans(beanDependencyNames);
        var bd = (AbstractJsonBeanDefinition)beanDefinition;
        var constructorArgConfigurations = bd.getConstructorArgs();
        var propertyConfigurations = bd.getProperties();
        Object bean = null;
        if (CollectionUtils.hasElement(constructorArgConfigurations))
            bean = buildBeanByConstructor(beanDefinition.getName(),beanDefinition.getBeanClass(),constructorArgConfigurations);
        else if (CollectionUtils.hasElement(propertyConfigurations))
            bean = buildBeanByProperties(beanDefinition.getName(),beanDefinition.getBeanClass(),propertyConfigurations);
        else {
            try {
                bean = beanDefinition.getBeanClass().newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        bd.setBeanObject(bean);
        return bean;
    }
}
